﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JeanFish.Common.Model.Computer.Format.Audio
{
    /// <summary>
    /// 表示WAV文件的格式类型。
    /// </summary>
    public class Wave : RIFF
    {
        /// <summary>
        /// 表示WAV文件中的格式块。
        /// </summary>
        public sealed class FormatChunk : RIFFChunk
        {
            #region const
            private const byte FormatLength = IDLength;
            private const byte FormatIndex = 0;
            private const byte FormatTagLength = 2;
            private const byte FormatTagIndex = FormatLength + SizeLength;
            private const byte ChannelLength = 2;
            private const byte ChannelIndex = FormatTagIndex + FormatTagLength;
            private const byte SamplesPerSecondLength = 4;
            private const byte SamplesPerSecondIndex = ChannelIndex + ChannelLength;
            private const byte AverageBytesPerSecondLength = 4;
            private const byte AverageBytesPerSecondIndex = SamplesPerSecondIndex + SamplesPerSecondLength;
            private const byte BlockAlignLength = 2;
            private const byte BlockAlignIndex = AverageBytesPerSecondIndex + AverageBytesPerSecondLength;
            private const byte BitsPerSampleLength = 2;
            private const byte BitsPerSampleIndex = BlockAlignIndex + BlockAlignLength;
            private const byte AdditionalLength = 2;
            private const byte AdditionalIndex = BitsPerSampleIndex + BitsPerSampleLength;
            #endregion
            /// <summary>
            /// 初始化WAV文件中的格式块。
            /// </summary>
            /// <param name="wave">格式块所在的WAV文件</param>
            /// <param name="hasAdditional">指示格式块中是否有附加参数</param>
            public FormatChunk(Wave wave,bool hasAdditional)
                : base(wave, 12, (uint)(hasAdditional ? 26 : 24))
            {
                this.data[0]=(byte)'f';
                this.data[1] = (byte)'m';
                this.data[2] = (byte)'t';
                this.data[3] = (byte)' ';
            }
            /// <summary>
            /// 表示编码格式，值＝1表示编码方式为PCMμ律编码。
            /// </summary>
            public UInt16 FormatTag
            {
                get
                {
                    return BitConverter.ToUInt16(this.data.Skip(FormatTagIndex).Take(FormatTagLength).ToArray(),0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, FormatTagIndex, FormatTagLength);
                }
            }
            /// <summary>
            /// 表示声道数。
            /// </summary>
            public UInt16 Channels
            {
                get
                {
                    return BitConverter.ToUInt16(this.data.Skip(ChannelIndex).Take(ChannelLength).ToArray(), 0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, ChannelIndex, ChannelLength);
                }
            }
            /// <summary>
            /// 表示采样频率。
            /// </summary>
            public UInt32 SamplesPerSecond
            {
                get
                {
                    return BitConverter.ToUInt32(this.data.Skip(SamplesPerSecondIndex).Take(SamplesPerSecondLength).ToArray(), 0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, SamplesPerSecondIndex, SamplesPerSecondLength);
                }
            }
            /// <summary>
            /// 表示平均每秒数据传输速率。
            /// </summary>
            public UInt32 AverageBytesPerSecond
            {
                get
                {
                    return BitConverter.ToUInt32(this.data.Skip(AverageBytesPerSecondIndex).Take(AverageBytesPerSecondLength).ToArray(), 0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, AverageBytesPerSecondIndex, AverageBytesPerSecondLength);
                }
            }
            /// <summary>
            /// 表示每个样本字节数。
            /// </summary>
            public UInt16 BlockAlign
            {
                get
                {
                    return BitConverter.ToUInt16(this.data.Skip(BlockAlignIndex).Take(BlockAlignLength).ToArray(), 0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, BlockAlignIndex, BlockAlignLength);
                }
            }
            /// <summary>
            /// 表示每个样本比特数。
            /// </summary>
            public UInt16 BitsPerSample
            {
                get
                {
                    return BitConverter.ToUInt16(this.data.Skip(BitsPerSampleIndex).Take(BitsPerSampleLength).ToArray(), 0);
                }
                set
                {
                    this.data.Copy(BitConverter.GetBytes(value), 0, BitsPerSampleIndex, BitsPerSampleLength);
                }
            }
            /// <summary>
            /// 表示附加信息。
            /// </summary>
            public UInt16? Additional
            {
                get
                {
                    if (this.Size == 16) return null;
                    return BitConverter.ToUInt16(this.data.Skip(AdditionalIndex).Take(AdditionalLength).ToArray(), 0);
                }
                set
                {
                    if (value.HasValue)
                    {
                        this.Size = 18;
                        this.data.Copy(BitConverter.GetBytes(value.Value), 0, AdditionalIndex, AdditionalLength);
                    }
                    else
                    {
                        this.Size = 16;
                    }
                }
            }
        }
        /// <summary>
        /// 表示WAV文件中的附加数据块。
        /// </summary>
        public sealed class FactChunk : RIFFChunk
        {
            #region const
            private const byte FactIndex = IDIndex;
            private const byte FactLength = IDLength;
            private const byte DataIndex = 8;
            private const byte DataLength = 4;
            #endregion
            /// <summary>
            /// 初始化附加数据块。
            /// </summary>
            /// <param name="wave">格式块所在的WAV文件</param>
            /// <param name="chunkIndex">格式块开始索引</param>
            public FactChunk(Wave wave,int chunkIndex)
                :base(wave,chunkIndex,12)
            {
                this.data[0] = (byte)'f'; this.data[1] = (byte)'a'; this.data[2] = (byte)'c'; this.data[3] = (byte)'t';
            }
            /// <summary>
            /// 表示附加快数据。
            /// </summary>
            public byte[] Data
            {
                get { return this.data.Skip(DataIndex).Take(DataLength).ToArray(); }
                set
                {
                    this.data.Copy(value, 0, DataIndex, DataLength);
                }
            }
        }
        /// <summary>
        /// 表示WAV文中的数据块。
        /// </summary>
        public sealed class DataChunk : RIFFChunk
        {
            #region const
            private const byte RIFFDataIndex = IDIndex;
            private const byte RIFFDataLength = IDLength;
            private const byte DataIndex = 8;
            private const long DataLength = uint.MaxValue;
            #endregion
            /// <summary>
            /// 初始化数据块。
            /// </summary>
            /// <param name="wave">数据块所在的WAV文件</param>
            /// <param name="chunkIndex">数据块开始索引</param>
            /// <param name="chunkSize">数据块长度</param>
            public DataChunk(Wave wave,int chunkIndex,int chunkSize)
                :base(wave,chunkIndex,(uint)chunkSize)
            {
                this.data[0] = (byte)'d'; this.data[1] = (byte)'a'; this.data[2] = (byte)'t'; this.data[3] = (byte)'a';
            }
            /// <summary>
            /// 表示数据块中的数据。
            /// </summary>
            public byte[] Data
            {
                get { return this.data.Skip(DataIndex).Take((int)this.Size).ToArray(); }
                set
                {
                    this.Size =(uint)value.Length;
                    this.data.Copy(value, 0, DataIndex, (int)this.Size);
                }
            }
        }
        /// <summary>
        /// 根据文件长度、有无附加格式，有无附加块初始化WAV文件。
        /// </summary>
        /// <param name="fileSize">文件长度</param>
        /// <param name="hasAdditionalFormat">有无附加格式</param>
        /// <param name="hasFactChunk">有无附加块</param>
        public Wave(uint fileSize, bool hasAdditionalFormat, bool hasFactChunk)
            : base(new char[] { 'W', 'A', 'V', 'E' }, fileSize)
        {
            this.Format = new FormatChunk(this, hasAdditionalFormat);
            this.Fact = null;
            hasFactChunk = this.Format.Size == 18;
            if (hasFactChunk)
                this.Fact = new FactChunk(this, (int)(12 + this.Format.Size + 8));
            int dataIndex=(int)(12 + this.Format.Size + 8+(hasFactChunk?this.Fact.Size+8:0));
            this.Data = new DataChunk(this, dataIndex, (int)fileSize - dataIndex);
        }
        /// <summary>
        /// 从文件字节数据初始化WAV文件。
        /// </summary>
        /// <param name="data"></param>
        public Wave(byte[] data)
            :base(new char[] { 'W', 'A', 'V', 'E' },data)
        {
            this.Format = new FormatChunk(this, true);
            this.Fact = null;
            bool hasFactChunk = this.Format.Size == 18;
            if (hasFactChunk)
                this.Fact = new FactChunk(this, (int)(12 + this.Format.Size + 8));
            int dataIndex = (int)(12 + this.Format.Size + 8 + (hasFactChunk ? this.Fact.Size + 8 : 0));
            this.Data = new DataChunk(this, dataIndex, (int)data.Length - dataIndex);
        }
        /// <summary>
        /// 表示WAV文件格式块。
        /// </summary>
        public FormatChunk Format
        {
            get;
            private set;
        }
        /// <summary>
        /// 表示WAV文件附加格式块。
        /// </summary>
        public FactChunk Fact
        {
            get;
            private set;
        }
        /// <summary>
        /// 表示WAV文件数据块。
        /// </summary>
        public DataChunk Data
        {
            get;
            private set;
        }
    }
}